package tpase.client.Controller;

import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Pairing;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import tpase.client.Handler.ClientAuthHandler;
import tpase.client.Handler.ClientLoginHandler;
import tpase.client.Handler.ClientLoginToStorageHandler;
import tpase.client.Handler.ClientRegisterHandler;
import tpase.client.VO.Address;
import tpase.client.VO.PublicParam;
import tpase.client.VO.StatusCode;
import tpase.util.Util;

import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;


public class LoginController {

    //方案的参数
    public static int t, n;
    public static String uid, password;
    // 是否 已经初始化
    public static boolean isSetup = true;
    public static boolean isDebug = true;
    public static boolean isFirstVerifyFinal = true;

//    public static Client client;
    String Default_UserPath = System.getProperty("user.dir")+"/Save/";//默认下载地址（TPaSer-User下的Save文件夹  ）
    //


    public Label login_error;//反馈用户登录错误信息
    public Label register_error;//反馈用户注册错
    public TextArea register_msg;
    public ProgressBar register_progress;

    @FXML
    private Parent rootLayout;
    private double offsetX;
    private double offsetY;
    @FXML
    private TextField login_uid;
    @FXML
    private PasswordField login_password;
    public TextField register_uid;
    public PasswordField register_password;
    public PasswordField register_password1;


    private static List<Address> serverList;
    private static Address storageServerAddress;

    private static String path = "src/main/resources/config";
    private static String publicParamPath = path + "/publicParam.json";
    private static PublicParam publicParam;
    private static Pairing pairing;

    private void loadParam() {
        pairing = Util.getPairing("src/main/resources/config/a.properties");
        publicParam = Util.jsonFileToObj(publicParamPath, PublicParam.class);
        t = publicParam.getMap().get("t")[0];
        n = publicParam.getMap().get("n")[0];
        String serverListJson = new String(publicParam.getMap().get("serverList"));
        serverList = Util.jsonToObject(serverListJson, ArrayList.class, Address.class);
        String storageServerPath = "src/main/resources/config/storageServerParameters.json";
        storageServerAddress = Util.jsonFileToObj(storageServerPath, Address.class);
    }

    // 用户注册事件
    public void onRegisterButtonClick(ActionEvent actionEvent) throws InterruptedException, IOException {
        if (register_uid.getText().isEmpty()) {
            register_error.setTextFill(javafx.scene.paint.Color.RED);
            register_error.setText("用户名不能为空!");
            return;
        }
        if (register_password.getText().isEmpty()) {
            register_error.setTextFill(javafx.scene.paint.Color.RED);
            register_error.setText("密码不能为空!");
            return;
        }

        // 判断两次输入的密码是否一致
        if (!register_password.getText().equals(register_password1.getText())) {
            //弹出错误窗口
            register_error.setTextFill(javafx.scene.paint.Color.RED);
            register_error.setText("两次输入的密码不一致!");
            return;
        }

        loadParam();

        uid = register_uid.getText();
        password = register_password.getText();
        List<Element> Y_1iList = new CopyOnWriteArrayList<>();

        // 数量为t的集合，用于计算秘密共享密钥
        List<Address> thresholdList = getThresholdList(serverList, t);
        // 发送给服务器 计算 lambda_{0, i, T}
        List<byte[]> idList = getIdList(thresholdList);

        Element r_pwd = pairing.getZr().newRandomElement();

        Element x_pwd = Util.hash(pairing.getG1(), password).powZn(r_pwd);

        Object lock = new Object();
        List<Boolean> isExisting = new ArrayList<>();
        isExisting.add(false);
        for (int i = 0; i < t; i++) {
            Address serverAddress = thresholdList.get(i);
            new Thread(() -> {
                Util.processClientBootstrap(new ClientRegisterHandler(path, uid, x_pwd, idList, Y_1iList, serverAddress, lock, t, isExisting), serverAddress);
            }).start();

        }

        synchronized (lock) {
            while (Y_1iList.size() < t && isExisting.get(0) == false) {
                lock.wait();
            }
            lock.notifyAll();
        }
        if (isExisting.get(0)) {
            register_error.setTextFill(javafx.scene.paint.Color.RED);
            register_error.setText("用户名已存在!");
            return;
        }

        List<Element> xServerList = new ArrayList<>();
        for (byte[] bytes : idList) {
            xServerList.add(pairing.getZr().newElementFromBytes(bytes));
            System.out.println("register x : " + pairing.getZr().newElementFromBytes(bytes));
        }

        Element Y_1 = Util.getY1iWithoutLambda(Y_1iList, r_pwd, pairing);

        Object registerLock = new Object();
        List<Boolean> regSuccessList = new CopyOnWriteArrayList<>();

        // 用户将 特定于密钥服务器的用户身份认证信息 发送给 密钥服务器
        // auth_{uid, i} = H_3(Y_1, i)
        for (int i = 0; i < serverList.size(); i++) {
            byte[] id_bytes = serverList.get(i).getId();
            String Y_1_i = Y_1.toString() + pairing.getZr().newElementFromBytes(id_bytes).toString();
            Element auth_uid_i = Util.hash(pairing.getZr(), Y_1_i);
            Address serverAddress = serverList.get(i);
            System.out.printf("用户[%s]  ----auth{uid,KS_i}--->  服务器[%s]：\n\tauth{uid,KS_i}[%s]\n",//添加了彩色可突出通信数据  "+"\033[33;1m" + "----auth{uid,KS_i}--->" + "\033[0m"+"
                    uid,serverAddress.toString(), Arrays.toString(auth_uid_i.toBytes()));
            new Thread(() -> {
                Util.processClientBootstrap(new ClientAuthHandler(uid, auth_uid_i, registerLock, regSuccessList), serverAddress);
            }).start();
        }


        // H(Y_1 | -1) 作为 发送给 StorageServer 的 用户认证信息
        String Y_1_storage = Y_1.toString() + pairing.getZr().newElementFromBytes(new byte[]{-1}).toString();
        Element auth_uid_storage = Util.hash(pairing.getZr(), Y_1_storage);

        System.out.println("ref \t Y_1 : " + Y_1);
        System.out.println("reg \t H(Y_1 | -1) : " + auth_uid_storage);

        new Thread(() -> {
            Util.processClientBootstrap(new ClientAuthHandler(uid, auth_uid_storage, registerLock, regSuccessList), storageServerAddress);
        }).start();

        synchronized (registerLock) {
            while (regSuccessList.size() < n + 1) {
                registerLock.wait();
            }
            registerLock.notifyAll();
        }
        int tmpIndex = 0;
        for (; tmpIndex < n + 1; tmpIndex++) {
            if (regSuccessList.get(tmpIndex) == false) {
                break;
            }
        }
        if (tmpIndex == n + 1) {
            register_error.setTextFill(javafx.scene.paint.Color.GREEN);
            register_error.setText("注册成功!");
        } else {
            register_error.setTextFill(javafx.scene.paint.Color.RED);
            register_error.setText("注册失败!");
        }
    }


    private static List<Address> getThresholdList(List<Address> serverList, int t) {
        List<Address> list = new ArrayList<>(serverList);
        // 打乱顺序，n个服务器中任意选t个服务器
        Collections.shuffle(list);

        return list.subList(0, t);
    }

    private static List<byte[]> getIdList(List<Address> thresholdList) {
        List<byte[]> idList = new ArrayList<>();
        for (int i = 0; i < thresholdList.size(); i++) {
            idList.add(thresholdList.get(i).getId());
        }
        return idList;
    }


    // 用户登录事件
    public void onLoginButtonClick(ActionEvent actionEvent) throws IOException, InterruptedException {
        if (login_uid.getText().isEmpty()) {
            login_error.setTextFill(javafx.scene.paint.Color.RED);
            login_error.setText("用户名不能为空!");
            return;
        }
        if (login_password.getText().isEmpty()) {
            login_error.setTextFill(javafx.scene.paint.Color.RED);
            login_error.setText("密码不能为空!");
            return;
        }

        loadParam();

        uid = login_uid.getText();
        password = login_password.getText();

        List<Element> Y_1iList = new CopyOnWriteArrayList<>();
        List<Element> xServerList = new CopyOnWriteArrayList<>();

        // 数量为t的集合，用于计算秘密共享密钥
        List<Address> thresholdList = getThresholdList(serverList, t);
        // 发送给服务器 计算 lambda_{0, i, T}
        List<byte[]> idList = getIdList(thresholdList);

        Element r_pwd = pairing.getZr().newRandomElement();

        Element x_pwd = Util.hash(pairing.getG1(), password).powZn(r_pwd);

        Object lock = new Object();
        List<Boolean> isExisting = new ArrayList<>();
        isExisting.add(true);
        for (int i = 0; i < t; i++) {
            Address serverAddress = thresholdList.get(i);
            new Thread(() -> {
                Util.processClientBootstrap(new ClientLoginHandler(path, uid, x_pwd, idList, Y_1iList, serverAddress, lock, t, isExisting, xServerList), serverAddress);
            }).start();
        }

        synchronized (lock) {
            while (Y_1iList.size() < t && isExisting.get(0) == true) {
                lock.wait();
            }
            lock.notifyAll();
        }
        if (isExisting.get(0) == false) {
            login_error.setTextFill(javafx.scene.paint.Color.RED);
            login_error.setText("用户名不存在!");
            return;
        }

        Element Y_1 = Util.getY1(Y_1iList, r_pwd, xServerList, pairing);

        System.out.println(Y_1);

        // H(Y_1 | -1) 作为 发送给 StorageServer 的 用户认证信息，用于用户登录的身份认证
        String Y_1_storage = Y_1.toString() + pairing.getZr().newElementFromBytes(new byte[]{-1}).toString();
        Element auth_uid_storage = Util.hash(pairing.getZr(), Y_1_storage);

        System.out.println("login \t H(Y_1 | -1) : " + auth_uid_storage);


        List<Integer> loginResultList = new CopyOnWriteArrayList<>();
        Util.processClientBootstrap(new ClientLoginToStorageHandler(uid, auth_uid_storage, loginResultList), storageServerAddress);

        Integer loginResultCode = loginResultList.get(0);



        switch (loginResultCode) {
            case StatusCode.LOGIN_NO_UID_RESPONSE:
                setErrorLabel(login_error, "用户名不存在！");
                return;
            case StatusCode.LOGIN_PWD_WRONG:
                setErrorLabel(login_error, "密码错误！");
                return;
            case StatusCode.LOGIN_RESPONSE:
                setSuccessLabel(login_error, "登录成功！");
                break;
        }

            //传递uid 和password给MainController
            uid = login_uid.getText();
            password = login_password.getText();

            // 登录成功，在存储服务器存储用户信息

            final double mainWidth = 1100.0;
            final double mainHeight = 700.0;

            switch_mainScene("/tpase/client/main-view.fxml", mainWidth, mainHeight);

    }

    // 跳转到用户注册界面
    public void onToRegisterButtonClick() throws IOException {
        switchScene("/tpase/client/register-view.fxml", 320.0, 330.0);
    }

    public void switch_mainScene(String fxmlName, double width, double height) throws IOException {

        Stage stage = (Stage) rootLayout.getScene().getWindow();

        FXMLLoader fxmlLoader = new FXMLLoader(LoginController.class.getResource(fxmlName));
        Scene scene = new Scene(fxmlLoader.load(), width, height);
        stage.setScene(scene);
        stage.centerOnScreen();

        stage.show();
    }


    // 跳转到登录页面
    public void onToLoginButtonClick(MouseEvent mouseEvent) throws IOException {
        switchScene("/tpase/client/login-view.fxml", 320.0, 280.0);
    }

    // 切换场景Scene
    private void switchScene(String fxmlName, double width, double height) throws IOException {
        Stage stage = (Stage) rootLayout.getScene().getWindow();
        FXMLLoader fxmlLoader = new FXMLLoader(LoginController.class.getResource(fxmlName));
        Scene scene = new Scene(fxmlLoader.load(), width, height);
        stage.setScene(scene);
        stage.centerOnScreen();
        stage.show();
    }

    // 关闭应用
    public void windowClose(ActionEvent actionEvent) {
        Stage stage = (Stage) rootLayout.getScene().getWindow();
        stage.close();
    }

    public void onMousePressed(MouseEvent mouseEvent) {
        offsetX = mouseEvent.getSceneX();
        offsetY = mouseEvent.getSceneY();
    }

    public void onMouseDragged(MouseEvent mouseEvent) {
        Stage stage = (Stage) rootLayout.getScene().getWindow();
        stage.setX(mouseEvent.getScreenX() - offsetX);
        stage.setY(mouseEvent.getScreenY() - offsetY);
        stage.setOpacity(0.9);
    }

    // 移动窗口位置，鼠标释放事件
    public void onMouseReleased(MouseEvent mouseEvent) {
        Stage stage = (Stage) rootLayout.getScene().getWindow();
        stage.setOpacity(1.0);
    }

    private void setSuccessLabel(Label label, String text) {
        label.setTextFill(javafx.scene.paint.Color.GREEN);
        label.setText(text);
    }

    public void setErrorLabel(Label label, String text) {
        label.setTextFill(javafx.scene.paint.Color.RED);
        label.setText(text);
    }
    public static void setup() throws InterruptedException {
//        if (isSetup == false) {
//            Setup.getSetup().start();
//        }
//        //读取公共参数
//        String setupPath = "src/main/resources/config/setup/parameters.json";
//        SetupParam setupParam = org.tpase.util.Util.jsonFileToObj(setupPath, SetupParam.class);
//        t = setupParam.getT();
//        n = setupParam.getN();
//
//        if (isDebug) {
//            System.out.printf("总服务器数量：%s，门限服务器数量：%s \n", n, t);
//        }
//
//
//        String serverPath = "src/main/resources/config/servers/server";
//        List<Server> serverLists = new ArrayList<>();
//        for (int i = 0; i < n; i++) {
//            String paramPath = serverPath + i;
//            Server server = new Server(paramPath, isDebug);
//            if (isSetup == false) {
//                server.setup();
//            }
//            serverLists.add(server);
//        }
//
//        if (isSetup == false) {
//            Thread.sleep(2000);
//        }
//
//        //多线程并行计算
//        for (int i = 0; i < n; i++) {
//            Server server = serverLists.get(i);
//            new Thread(server::start).start();
//        }
    }

    private static void register(String uid, String password) {
//        String clientPath = "src/main/resources/config/clients/client0";
//        client = new Client(clientPath, uid, password, isDebug, isFirstVerifyFinal);
//        if (isSetup == false) {
//            client.setup();
//        }
//        client.register();

    }

    public static void main(String[] args) {
        int t = 2, n = 3;
        // 测试 秘密共享 恢复
        Pairing pairing1 = Util.getPairing("src/main/resources/config/a.properties");
        List<Element> xList = new ArrayList<>();
        xList.add(pairing1.getZr().newElement(new BigInteger("622579566978585141836704869377591493883710218260")));
        xList.add(pairing1.getZr().newElement(new BigInteger("276675603025423789746954673733555802535657665382")));
        xList.add(pairing1.getZr().newElement(new BigInteger("7815360390755143584626952028276912087288230730")));

        List<Element> siList = new ArrayList<>();
        siList.add(pairing1.getZr().newElement(new BigInteger("88555110068232330225188636321593337938999589475")));
        siList.add(pairing1.getZr().newElement(new BigInteger("116282796704077473595462255836696808495277410866")));
        siList.add(pairing1.getZr().newElement(new BigInteger("131342232396726470464529885095453995659190679118")));



        uid = "t1";
        password = "1";
        System.out.println("login: " + uid + ", " + password);


        List<Element> Y_1iList = new CopyOnWriteArrayList<>();

        Element r_pwd = pairing.getZr().newRandomElement();

        Element x_pwd = Util.hash(pairing.getG1(), password).powZn(r_pwd);


        List<Element> xServerList = new ArrayList<>();
        int skipIdx = 1;

        for (int i = 0; i < n; i++) {
            if (i == skipIdx) {
                continue;
            }
            Element s_i = siList.get(i);
            Element Y_1i = Util.getY1iWithoutLambda(uid, x_pwd, pairing, s_i);
            Y_1iList.add(Y_1i);

            xServerList.add(xList.get(i));
        }


        Element Y_1 = Util.getY1(Y_1iList, r_pwd, xServerList, pairing);


        // H(Y_1 | -1) 作为 发送给 StorageServer 的 用户认证信息，用于用户登录的身份认证
        String Y_1_storage = Y_1.toString() + pairing.getZr().newElementFromBytes(new byte[]{-1}).toString();
        Element auth_uid_storage = Util.hash(pairing.getZr(), Y_1_storage);







    }
}
